package com.learn.designPatterns.createType.singleton;

/**
 *
 * @Description: 双重校验锁-线程安全
 * @author: yfpu
 * @Date: 2018-08-21 17:45
 */
public class Singleton4_Double_Lock {

    /**
     * uniqueInstance 只需要被实例化一次，之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行，只有当
     * uniqueInstance没有被实例化时，才需要进行加锁.
     *
     * 双重校验锁先判断uniqueInstance是否已经被袜化化，如果没有被实例化，那么才对实例化语句进行加锁
     */

    private volatile static Singleton4_Double_Lock unquiqueInstance = null;

    private Singleton4_Double_Lock() {
    }

    public static Singleton4_Double_Lock getUnquiqueInstance() {
        if (unquiqueInstance == null) {
            synchronized (Singleton4_Double_Lock.class) {
                if (unquiqueInstance == null)
                    unquiqueInstance = new Singleton4_Double_Lock();
            }
        }
        return unquiqueInstance;
    }


    /**
     * 为什么要进行两次 == null 判断？
     *
     *
     * 考虑下面的实现，也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下，如果两 * 个线程同时执
     * 行 if 语句，那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作， * 但是两个线程都 * 会
     * 执行 uniqueInstance = new Singleton(); 这条语句，只是先后的问题，那么就会进行两次
     * 实例化，从而产生了两个实例。因此必须使用双重校验锁，也就是需要使用两个 if 语句。
     */


    /**
     * uniqueInstance 采用 volatile 养分字修饰也是很有必要的。 uniqueInstance = new Singleton();这段代码
     * 其实是分为三步执行。
     * 1.分配内存空间
     * 2.初始化对象。
     * 3.将 uniqueInstance 指向分配的内存地址
     *
     * 但是由于 JVM 具有指令重排的特性，有可能执行顺序变为了1>3>2, 这在单线程情况下自然是没有问题的。但如果是多
     * 线程下，有可能获得是一个还湍爱疲初始化的实例，以至于程序出错。
     *
     * 使用 volatile 可以禁止 JVM 的指令重排，保证在多线程环境下也能正常运行。
     */
}
